﻿namespace Dcm.Tests
{
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Xml.Linq;

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using DynamicConfigurationManager;

    internal class Utilities
    {
        #region Constants and Fields

        public static string ConfigMap = DynamicConfigurationSectionHandler.ConfigMapKey;
        public const string UnreliableTest = "not a unit test, not reliable for repeatable/automated unit tests";
        #endregion

        #region Methods

        internal static void AssertWithLog<T>(Action<T, T, string> assertionAction, T expected, T actual)
        {
            assertionAction(expected, actual, FailureMessageWithLog(expected, actual));
        }

        internal static string CatchInconclusive<T>(string expected, Func<string> actualFunc) where T : Exception
        {
            try
            {
                string actual = actualFunc();
                if (expected != actual)
                {
                    Assert.Inconclusive(UnreliableTest);
                }
                return actual;
            }
            catch (T)
            {
                Assert.Inconclusive(UnreliableTest);
            }
            return null;
        }

        internal static XElement GenerateFakeConfig(XElement innerXml)
        {
            return new XElement("configuration", innerXml);
        }

        internal static XElement GenerateFakeDcmConfig(XElement innerXml)
        {
            return GenerateFakeConfig(new XElement("DynamicConfigurationSection", innerXml));
        }

        internal static XElement GenerateFakeDcmWithMapMatch(
            XElement configInnerXml = null, XElement dcmInnerXml = null, params object[] mapContent)
        {
            XElement config = GenerateFakeConfig(configInnerXml);

            var dcm = new XElement("DynamicConfigurationSection");

            config.Add(dcm);
            var configMap = new XElement(ConfigMap, mapContent);
            configMap.SetAttributeValue("hostname", Environment.MachineName);
            dcm.Add(configMap);
            if (dcmInnerXml != null)
            {
                dcm.Add(dcmInnerXml);
            }
            return config;
        }

        internal static XElement GenerateNameValueElement(string key, string value)
        {
            return new XElement("add", new XAttribute("key", key), new XAttribute("value", value));
        }

        internal static string MachineNameWildcard()
        {
            return Environment.MachineName.Substring(0, 1) + "*";
        }

        internal static string ProcessFakeConfig(XElement config, string key)
        {
            NameValueCollection section = DynamicConfigurationManager.GetSection(config, null);
            return section[key];
        }

        internal static string ProcessFakeConfig(
            string expectedValue, IEnumerable<KeyValuePair<Type, string>> mapsWithMapMatch)
        {
            const string Key = "key1";
            var configMap = new XElement(
                ConfigMap, new XElement("add", new XAttribute("key", Key), new XAttribute("value", expectedValue)));
            foreach (var arg in mapsWithMapMatch)
            {
                XAttribute mapFilter = MakeMapFilter(GetTypeName(arg.Key), arg.Value);
                configMap.Add(mapFilter);
            }
            var config = new XElement("configuration", new XElement("DynamicConfigurationSection", configMap));
            return ProcessFakeConfig(config, Key);
        }

#if FAKES

        internal static string ProcessFakeConfig<T>(string mapMatch, string expectedValue)
            where T : IConfigMapAttribute_Accessor
        {
            string name = typeof(T).Name.Replace("_Accessor", string.Empty);
            string key = name + "key1";
            var configMap = new XElement(
                ConfigMap, new XElement("add", new XAttribute("key", key), new XAttribute("value", expectedValue)));
            configMap.Add(new XAttribute(name, mapMatch));
            XElement config = GenerateFakeDcmConfig(configMap);
            NameValueCollection section = DynamicConfigurationManager.GetSection(config, null);
            return section[key];
        }

#endif

        /// <summary>
        /// For testing config reading outside of matched set logic
        /// </summary>
        /// <param name="configContent"></param>
        /// <param name="dcmInnerXml"></param>
        /// <returns></returns>
        internal XElement GenerateFakeDcmWithEmptyMapMatch(XElement configContent = null, XElement dcmInnerXml = null)
        {
            XElement config = GenerateFakeConfig(configContent);
            var dcm = new XElement("DynamicConfigurationSection");
            config.Add(dcm);
            var configMap = new XElement(ConfigMap);
            dcm.Add(configMap);
            if (dcmInnerXml != null)
            {
                dcm.Add(dcmInnerXml);
            }
            configMap.Add(new XAttribute("HostName", Environment.MachineName));
            return config;
        }

        private static string FailureMessageWithLog<T>(T expected, T actual)
        {
            return String.Format("Expected:<{0}>. Actual:<{1}>. {2}", expected, actual, DynamicConfigurationManager.Log);
        }

        private static string GetTypeName(Type type)
        {
            return type.Name.Replace("_Accessor", string.Empty);
        }

        private static XAttribute MakeMapFilter(string typeName, string attributeMatchValue)
        {
            string name = typeName.Replace("_Accessor", string.Empty);
            return new XAttribute(name, attributeMatchValue);
        }

#endregion
    }
}